package fr.cephb.locustree;

import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.File;


import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

import fr.cephb.berkeleydb.db.Walker;


public class LTAscii
	{
	private static final Logger LOG=Logger.getLogger("fr.cephb");
	private static final int DEFAULT_PANE_WIDTH=80;

	private LTModel model;
	private int chromosome_id;
	private int chrom_start=0;
	private int chrom_length=0;
	private int panelWidth=DEFAULT_PANE_WIDTH;
	
	/**
	 * LTScannerImpl
	 *
	 */
	private class LTScannerImpl
		implements LTScanner
		{
		int track_id=-1;
		int max_histogram_count=0;
		int max_object_length=0;
		int histogram_counts[];
		
		
		LTScannerImpl()
			{
			this.histogram_counts=new int[ LTAscii.this.panelWidth];
			Arrays.fill(this.histogram_counts,0);
			}
		
		@Override
		public int getStart()
			{
			return Math.max( LTAscii.this.chrom_start-max_object_length , 0);
			}
		
		@Override
		public int getTrackId()
			{
			return this.track_id;
			}
		
		@Override
		public boolean isAbortCalled()
			{
			return false;
			}
		
		@Override
		public int getResolution()
			{
			return Math.max(LTAscii.this.chrom_length/LTAscii.this.panelWidth,1);
			}
		
		@Override
		public int getLength()
			{
			return LTAscii.this.chrom_length+max_object_length;
			}
		
		@Override
		public int getChromosomeId()
			{
			return LTAscii.this.chromosome_id;
			}
		
		@Override
		public void entity(MappedObject entity)
			{
			LTAscii.this.entity(this,entity);
			}
		
		@Override
		public void histogram(LTNode root)
			{
			LTAscii.this.histogram(this,root);
			}
		}
	
	/**
	 * LTAscii
	 */
	public LTAscii(LTModel model,
		int chromosome_id,
		int chrom_start,
		int chrom_length
		)
		{
		this.model=model;
		this.chrom_start=chrom_start;
		this.chrom_length=chrom_length;
		this.chromosome_id=chromosome_id;
		System.out.println(">"+model.getChromDB().get(null,chromosome_id).getName()+":"+this.chrom_start+"-"+(this.chrom_length+this.chrom_start));
		}
	
	public LTModel getModel()
		{
		return model;
		}
	
	protected LTScannerImpl createScannerForTrack(final int trackId)
		{
		LTScannerImpl scanner= new LTScannerImpl();
		scanner.track_id=trackId;
		return scanner;
		}
	
	public void scanTrack(Transaction txn, final int trackId)
		throws DatabaseException
		{
		TrackChrom tc=new TrackChrom(trackId,this.chromosome_id);
		Long rootId= getModel().getTrackChrom2NodeId().get(txn, tc);
		if(rootId==null || rootId==LTNode.NIL)
			{
			LOG.info("No root ID for "+tc);
			return;
			}
		System.out.println("trackId:"+trackId);
		LTNode root= getModel().findNodeById(txn, rootId);
		root.getMaxEntitiesLength();
		LTScannerImpl scanner= createScannerForTrack(trackId);
		scanner.max_object_length= root.getMaxEntitiesLength();
		getModel().scan(txn,scanner);
		drawHistogram(txn,trackId,scanner);
		}
	
	protected String getURLForObject(MappedObject o)
		{
		return null;
		}
	
	protected String getStyleForObject(MappedObject o)
		{
		return null;
		}
	
	
	protected void drawHistogram(Transaction txn,int trackId,LTScannerImpl scanner)
		{
		final int histogram_height=10;
		if(scanner.max_histogram_count==0) return;
		System.out.println("Histogram("+getModel().getTracksDB().get(txn,trackId).getName()+"): max:"+scanner.max_histogram_count);
		for(int y=histogram_height;y>=0;--y)
			{
			for(int x=0;x< this.panelWidth;++x)
				{
				double h= ((scanner.histogram_counts[x]/(double)scanner.max_histogram_count))*histogram_height;
				System.out.print(h>y?'#':' ');
				}
			System.out.println();
			}
		}
	
	
	protected int base2pixel(final int genome_pos)
		{
		return (int)(((genome_pos- this.chrom_start)/(double)this.chrom_length)*this.panelWidth);
		}
	
	protected void entity(LTScannerImpl scanner,final MappedObject entity)
		{
		assert(entity.getStart()<= entity.getEnd());
		if(entity.getEnd()< this.chrom_start) return;
		if(entity.getStart()>= (this.chrom_start+this.chrom_length)) return;
		int x1=base2pixel(entity.getStart());
		if(x1<0) x1=0;
		int x2=base2pixel(entity.getEnd());
		if(x2>panelWidth) x2=panelWidth;
		if(x2==x1) x2++;
		assert(x1>=0);
		assert(x2>=x1);
		StringBuilder left= new StringBuilder(x1);
		StringBuilder mid= new StringBuilder(x2-x1);
		
		for(int i=0;i< x1;++i)left.append(' ');
		for(int i=x1;i<x2;++i) mid.append('=');
		
		int name_length=entity.getName().length();
		if(name_length+1< left.length())
			{
			left.replace(
				(left.length()-1)-(name_length),
				left.length()-1,
				entity.getName()
				);
			}
		else if(mid.length()>=name_length+2)
			{
			mid.replace(
					1,
					name_length,
					entity.getName()
					);
			}
		else
			{
			mid.append(" ").append(entity.getName());
			}
		System.out.print(left);
		System.out.print(mid);
		//System.out.print(" "+entity.getStart()+" "+entity.getEnd());
		System.out.println();
		}
	
	protected void histogram(LTScannerImpl scanner,LTNode root)
		{
		final float base_per_histo=(float)this.chrom_length/(float)this.panelWidth;
		float base_index= this.chrom_start;
		for(int i=0;i< this.panelWidth;++i)
			{
			if(base_index + base_per_histo < root.getStart())
				{
				base_index+=base_per_histo;
				continue;
				}
			if(base_index >= root.getEnd()) break;
			scanner.histogram_counts[i]+=root.getCountEntitiesBelow();
			scanner.max_histogram_count=Math.max(scanner.max_histogram_count, scanner.histogram_counts[i]);
			base_index+=base_per_histo;
			}
		}
	
		/**
		 * main
		 */
		public static void main(String[] args)
			{
			LTModel model=null;
			final Transaction txn=null;
		try
			{
			int start=0;
			int end=Integer.MAX_VALUE;
			String orgName=null;
			String chromName=null;
			String dbHome=null;
			int optind=0;
			while(optind< args.length)
				{
				if(args[optind].equals("-h") ||
				   args[optind].equals("-help") ||
				   args[optind].equals("--help"))
					{
					System.err.println("Options:");
					System.err.println(" -h help; This screen.");
					System.err.println(" -db-home <berkeley-db-home>");
					System.err.println(" -o <org-name>");
					System.err.println(" -c <chrom-id>");
					System.err.println(" -s <start> default:0");
					System.err.println(" -e <end> default:"+end);
					return;
					}
				else if(args[optind].equals("-db-home"))
					{
					dbHome=args[++optind];
					}
				else if(args[optind].equals("-o") || args[optind].equals("-org") || args[optind].equals("-organism"))
					{
					orgName=args[++optind];
					}
				else if(args[optind].equals("-s") || args[optind].equals("-start"))
					{
					start=Integer.parseInt(args[++optind]);
					}
				else if(args[optind].equals("-e") || args[optind].equals("-end"))
					{
					end=Integer.parseInt(args[++optind]);
					}
				else if(args[optind].equals("-c") || args[optind].equals("-chrom"))
					{
					chromName=args[++optind];
					}
				else if(args[optind].equals("-log-level"))
					{
					LOG.setLevel(Level.parse(args[++optind]));
					}
				else if(args[optind].equals("--"))
					{
					optind++;
					break;
					}
				else if(args[optind].startsWith("-"))
					{
					System.err.println("Unknown option "+args[optind]);
					return;
					}
				else 
					{
					break;
					}
				++optind;
				}
			if(dbHome==null)
				{
				System.err.println("db-home not specified.");
				return;
				}
			if(orgName==null)
				{
				System.err.println("organism not specified.");
				return;
				}
			if(chromName==null)
				{
				System.err.println("chromosome not specified.");
				return;
				}
			
			if(start>=end)
				{
				System.err.println("Bad start/end");
				return;
				}
			
			model= new LTModel();
			model.open(new File(dbHome), false);
			
			Organism organism= model.getOrganismByName(txn,orgName);
			if(organism==null)
				{
				System.err.println("Unknown organism:"+orgName);
				return;
				}

			ChromInfo ci=model.getChromsomeByName(txn,organism.getId(),chromName);
			
			if(ci==null)
				{
				System.err.println("Model doesn't contains \""+chromName+"\"");
				return;
				}
			if(end> ci.getLength())
				{
				end=ci.getLength();
				}
			
			
			LTAscii svg= new LTAscii(model,ci.getId(), start, (end-start));
			
			Walker<Integer,Track> w= model.getTracksDB().openWalker(txn);
			while(w.getNext()==OperationStatus.SUCCESS)
				{
				LOG.info("Scanning "+w.getKey());
				svg.scanTrack(txn, w.getKey());
				}
			w.close();
			} 
		catch(Throwable err)
			{
			err.printStackTrace();
			}
		finally
			{
			if(model!=null) model.close();
			}
		}
	
	}
